package com.apeli.system.service.impl;

import cn.hutool.core.text.StrSplitter;
import com.apeli.common.core.domain.AjaxResult;
import com.apeli.common.utils.SecurityUtils;
import com.apeli.common.utils.bean.BeanUtils;
import com.apeli.common.utils.file.AliyunOssUtils;
import com.apeli.common.utils.spring.SpringUtils;
import com.apeli.system.domain.Article;
import com.apeli.system.domain.Comment;
import com.apeli.system.domain.CommentAttachment;
import com.apeli.system.domain.SensorWord;
import com.apeli.system.domain.vo.ArticleVO;
import com.apeli.system.domain.vo.ImageVO;
import com.apeli.system.mapper.ArticleMapper;
import com.apeli.system.service.ArticleService;
import com.apeli.system.service.CommentAttachmentService;
import com.apeli.system.service.CommentService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.DateUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 *
 */
@Service
@Slf4j
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article>
        implements ArticleService {

    private final CommentAttachmentService commentAttachmentService;

    private final AliyunOssUtils utils;
    private ThreadPoolTaskExecutor executor = SpringUtils.getBean("threadPoolTaskExecutor");

    private final MongoTemplate mongoTemplate;

    private List<SensorWord> sensorWordLists;

    private final CommentService commentService;

    @PostConstruct
    private void sensorWordInit() {
       this.sensorWordLists = mongoTemplate.findAll(SensorWord.class);
       log.info("敏感词汇加载完毕：{}",this.sensorWordLists);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public AjaxResult addArticle(ArticleVO articleVO) {
        Article article = new Article();
        String username = SecurityUtils.getUsername();
        Date date = new Date();
        BeanUtils.copyProperties(articleVO, article);
        article.setLikes(0L);
        article.setCollection(0);
        article.setTag(articleVO.getTag().stream().collect(Collectors.joining(",")));
        article.setUserId(SecurityUtils.getUserId());
        article.setCreateDate(date);
        article.setUpdateDate(date);
        article.setCreateBy(username);
        article.setUpdateBy(username);
        article.setTitle(sensorWordsReplace(article.getTitle()));
        article.setDescription(sensorWordsReplace(article.getDescription()));
        // 新增文章默认私有
        article.setAuthentic(false);
        article.setStatus("1");
        article.setContent(sensorWordsReplace(articleVO.getContent()));
        this.save(article);
        // 存入通用附件表
        List<CommentAttachment> list = articleVO.getImageUrlList().stream().map(s -> {
            CommentAttachment commentAttachment = new CommentAttachment();
            commentAttachment.setPath(s);
            commentAttachment.setSourceId(article.getId());
            commentAttachment.setSourceTable("article");
            commentAttachment.setType("image");
            return commentAttachment;
        }).collect(Collectors.toList());
        commentAttachmentService.saveBatch(list);
        return AjaxResult.success();
    }

    private String sensorWordsReplace(String contents) {
        // 敏感词汇过滤
        List<String> sensorWordList = sensorWordLists.stream().map(SensorWord::getContent).collect(Collectors.toList());
        List<List<String>> sensorWords = Lists.partition(sensorWordList, 2);
        AtomicReference<String> content = new AtomicReference<>(contents);
        CompletableFuture[] completableFutureList = sensorWords.stream().map(sourceData -> CompletableFuture.runAsync(() -> {
            log.info("线程："+ Thread.currentThread().getName() + "开始执行敏感词汇替换...");
            for (String words : sourceData) {
                // 文本内容替换
                synchronized(this) {
                    content.set(content.get().replaceAll(words, "*"));
                }
            }
        }, executor)).toArray(CompletableFuture[]::new);
        // 等待线程池执行完毕，只有执行完以后才能向下执行，否则敏感词汇没有替换完毕，导致替换遗漏
        CompletableFuture.allOf(completableFutureList).join();
        return content.get();
    }

    @Override
    public List<ArticleVO> listArticle(Long userId) {
        List<ArticleVO> articleVOList = this.lambdaQuery().orderByDesc(Article::getUpdateDate).list().stream()
                .filter(o -> {
                    if (Objects.nonNull(userId)) {
                        return Objects.equals(userId, o.getUserId());
                    } else {
                        return o.getAuthentic();
                    }
                }).map(article -> {
                    ArticleVO articleVO = new ArticleVO();
                    BeanUtils.copyProperties(article, articleVO);
                    List<String> imageUrls = commentAttachmentService
                            .lambdaQuery().eq(CommentAttachment::getSourceId, article.getId())
                            .eq(CommentAttachment::getSourceTable, "article")
                            .list()
                            .stream()
                            .map(commentAttachment -> {
                                return utils.generatePresignedUrl(commentAttachment.getPath());
                            }).collect(Collectors.toList());
                    articleVO.setImageUrlList(imageUrls);
                    // 查询所有的评论
                    List<Comment> comments = commentService.lambdaQuery().eq(Comment::getArticleId, article.getId()).list();
                    comments.forEach(comment -> comment.setAvatar(utils.generatePresignedUrl(comment.getAvatar())));
                    articleVO.setCommentList(comments);
                    return articleVO;
                }).collect(Collectors.toList());
        return articleVOList;
    }

    @Override
    public AjaxResult changeStatus(Long articleId, Integer status) {
        this.lambdaUpdate().eq(Article::getId, articleId).set(Article::getAuthentic, status).update();
        return AjaxResult.success();
    }

    @Override
    public ArticleVO queryArticleById(Long id) {
        ArticleVO articleVO = new ArticleVO();
        Article article = getById(id);
        BeanUtils.copyProperties(article,articleVO);
        List<String> imageUrls = commentAttachmentService
                .lambdaQuery().eq(CommentAttachment::getSourceId, id)
                .eq(CommentAttachment::getSourceTable, "article")
                .list()
                .stream()
                .map(CommentAttachment::getPath)
                .collect(Collectors.toList());
        List<ImageVO> imageVOList = imageUrls.stream().map(s -> {
            ImageVO imageVO = new ImageVO();
            imageVO.setImagePath(s);
            imageVO.setImageUrl(utils.generatePresignedUrl(s));
            return imageVO;
        }).collect(Collectors.toList());
        articleVO.setImageVOList(imageVOList);
        articleVO.setTag(StrSplitter.split(article.getTag(),",",true,true));
        return articleVO;

    }

    @Override
    public AjaxResult commentArticle(String content, Long id) {
        log.info("评论文章，请求参数{}，{}",content,id);
        // 当前评论人的头像
        String avatar = SecurityUtils.getLoginUser().getUser().getAvatar();
        Comment comment = new Comment();
        comment.setArticleId(id);
        comment.setAvatar(avatar);
        comment.setContent(content);
        comment.setReviewer(SecurityUtils.getUsername());
        comment.setUserId(SecurityUtils.getUserId());
        comment.setCreateBy(SecurityUtils.getUsername());
        comment.setCreateDate(new Date());
        comment.setContent(sensorWordsReplace(content));
        commentService.save(comment);
        return AjaxResult.success();
    }
}




